package gw.com.android.ui.views;

import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;

/**
 * A class, that can be used as a TouchListener on any view (e.g. a Button). It
 * cyclically runs a clickListener, emulating keyboard-like behaviour. First
 * click is fired immediately, next after initialInterval, and subsequent after
 * normalInterval.
 * 
 * <p>
 * Interval is scheduled after the onClick completes, so it has to run fast. If
 * it runs slow, it does not generate skipped onClicks.
 */
public class OnTouchHoldListener implements OnTouchListener {

	private static final int DEFAULT_INITIAL_HOLD_DURATION = 400; // 400ms
	private static final int DEFAULT_REPEAT_ACTION_TIMEOUT = 100; // 100ms

	private Handler mHandler = new Handler();

	private int mInitialInterval;
	private final int mNormalInterval;
	private final OnClickListener mClickListener;

	private Runnable mHandlerRunnable = new Runnable() {
		@Override
		public void run() {
			mHandler.postDelayed(this, mNormalInterval);
			mClickListener.onClick(mDownView);
		}
	};

	private View mDownView;

	/**
	 * @param initialInterval
	 *            The interval after first click event
	 * @param normalInterval
	 *            The interval after second and subsequent click events
	 * @param clickListener
	 *            The OnClickListener, that will be called periodically
	 */
	public OnTouchHoldListener(int initialInterval, int normalInterval,
			OnClickListener clickListener) {
		if (clickListener == null)
			throw new IllegalArgumentException("null runnable");
		if (initialInterval < 0 || normalInterval < 0)
			throw new IllegalArgumentException("negative interval");

		this.mInitialInterval = initialInterval;
		this.mNormalInterval = normalInterval;
		this.mClickListener = clickListener;
	}

	public OnTouchHoldListener(OnClickListener clickListener) {
		this(DEFAULT_INITIAL_HOLD_DURATION, DEFAULT_REPEAT_ACTION_TIMEOUT,
				clickListener);
	}

	public boolean onTouch(View view, MotionEvent motionEvent) {
		switch (motionEvent.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mHandler.removeCallbacks(mHandlerRunnable);
			mHandler.postDelayed(mHandlerRunnable, mInitialInterval);
			mDownView = view;
			mClickListener.onClick(view);
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			mHandler.removeCallbacks(mHandlerRunnable);
			mDownView = null;
			break;
		}
		return true;
	}

}
